home *** CD-ROM | disk | FTP | other *** search
- /////////////////////////////////////////////////////////////////////
- // pkmg.cpp: Length restricted huffman code generator routines.
- // Copyright (c) 1991 Azarona Software
- // All rights reserved.
- //
- // Implements the package-merge algorithm as described in the
- // paper "A Fast Algorithm for Optimal Length-Limited Huffman
- // Codes", Lawrence L. Larmore and Daniel S. Hirschberg, JACM,
- // Vol. 37, No.3, July 1990, pp. 464-473
- //
- // This code is my interpretation of the algorithm, (it's presented
- // rather abstractly in the paper). I hope I got it right.
- //
- // The straight-forward implementation of the algorithm consumes
- // lots of memory. This implemetation has been optimized as much
- // as I know how, reducing the memory usage from over 200k, to
- // around 64k. The authors Larmore and Hirschberg describe a way
- // to use much less space, but they don't give the full algorithm
- // and I couldn't make heads or tails out of it.
- //
- // -- Bryan Flamig
- /////////////////////////////////////////////////////////////////////
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <alloc.h>
- #include "pkmg.h"
-
- void record_deepest(pnode *p, char soln[])
- // soln[sym] will eventually contain the leaf depth for
- // symbol sym. So, for all symbols in the package p, we
- // we record the deepest level found for those symbols.
- {
- while (p) { // Go thru complete package
- if (p->lvl != -1) {
- // We have a leaf pnode, might replace level in solution set
- if (p->lvl > soln[p->sym]) soln[p->sym] = p->lvl;
- }
- p = p->n; // Next pnode of package
- }
- }
-
- int next_diadic(unsigned n)
- // Returns the power of two exponent for the first bit
- // in n having a 1 in it, scanning right to left
- {
- int p = 0;
- if (n == 0) { printf("Error: infinite diadic\n"); exit(1); }
- while(1) {
- if (n & 1) return p;
- n >>= 1;
- p++;
- }
- }
-
- int package_merge(long wts[], unsigned char smap[],
- int ns, int nl, char soln[])
- // The weigts wts[] must come in sorted in decreasing order. If
- // you don't do this, all bets are off. (The program might even
- // crash!) smap[] = the indirection array, where smap[i] is the
- // symbol value for the ith element. ns = total number of symbols
- // to work with, nl = max number of levels you're going to allow,
- // soln[] is the list of leaf depths for the symbols.
- // NOTE: keep ns * nl <= 4096.
- {
- // We alternate between these two nodesets, each of which
- // represents the candidates of a particular level of the
- // optimum nodeset. The packages on the nodesets are always
- // in order, smallest weight package first.
-
- nodeset c1(ns);
- nodeset c2(ns);
-
- // The current nodeset we're using
-
- nodeset *candidates;
-
- // X is the value that the width
- // of the optimum nodeset must be
-
- unsigned X = ns - 1;
-
- // Okay, now we have current level, current width (representing
- // only the power of two exponent of the width), the current
- // minimum width, and the success return value for the algorithm
-
- int level, width, min_width, rv;
-
- // Allocate room for nodesets
-
- #ifdef DEBUG
- printf("bytesleft = %lu\n", coreleft());
- #endif
-
- pnode::pal = new pnode_allocator(ns * nl + 2*ns);
-
- #ifdef DEBUG
- printf("nodesleft = %d\n", pnode::pal->room());
- printf("bytesleft = %lu\n", coreleft());
- #endif
-
- // Start with the deepest level
-
- level = nl;
- width = -level; // Just using the power of 2 exponent
-
- // Get minimum width from the next diadic value of X,
- // smallest comes first
-
- min_width = next_diadic(X); // Just using power of 2 exponent
-
- // Okay, initialize the nodeset for this level
- // (assumes c2 = empty nodeset)
-
- c1.setup_and_merge(c2, wts, smap, ns, level);
- candidates = &c1;
-
- while (X > 0) {
-
- #ifdef DEBUG
- printf("nodesleft = %d\n", pnode::pal->room());
- #endif
-
- if (candidates->isempty() || width > min_width) {
- rv = 0;
- goto get_out;
- }
-
- if (width == min_width) {
- // Delete smallest weight package from candidate nodeset, add to
- // to the solution nodeset
- pnode *p = candidates->remove_front();
- record_deepest(p, soln);
- pnode::rmv_package(p); // Delete the whole package
- X &= ~(1 << min_width); // Assumes min_width non-negative
- if (X == 0) { // We have solution!
- rv = 1;
- goto get_out;
- }
- min_width = next_diadic(X);
- }
-
- // Bundle up the candidates into packages, and then
- // merge them with the next highest level candidates
-
- candidates->package();
- if (level > 1) { // If at level 0, don't need to merge anymore
- if (candidates == &c1) { // Alternate between nodesets
- c2.setup_and_merge(*candidates, wts, smap, ns, level-1);
- candidates = &c2;
- }
- else {
- c1.setup_and_merge(*candidates, wts, smap, ns, level-1);
- candidates = &c1;
- }
- }
- width++;
- if (level > 0) level--;
- }
- get_out:
- delete pnode::pal;
- return rv;
- }
-
- void make_codes(char len[], unsigned code[], unsigned char smap[],
- int ns, int nl)
- // Given the leaf depths len[], (which also happen to be the
- // code lengths), compute the code for each symbol
- {
- int i;
-
- unsigned *start = new unsigned[nl+2];
- int *len_cnt = new int[nl+1];
-
- // Compute the number of codes at each level
-
- for (i=1; i<nl+1; i++) len_cnt[i] = 0;
- for (i = 0; i<ns; i++) len_cnt[len[i]]++;
-
- // Compute the starting code for each level
-
- start[1] = 0;
- for (i = 1; i <= 16; i++)
- start[i + 1] = (start[i] + len_cnt[i]) << 1;
-
- // Compute the code for each symbol, mapping back to
- // proper indices while we're at it
-
- for (i = 0; i < ns; i++)
- code[smap[i]] = (start[len[i]]++) << (16-len[i]);
-
- delete start;
- delete len_cnt;
- }
-
- void qs(long wts[], unsigned char smap[], int n)
- // This specialized quick sort is 60 times faster than
- // Turbo C++'s generic qsort() for n = 256
- {
- int i, j, l, r;
- long tw, vw;
- unsigned char ts;
- int *top, *stk;
-
- stk = new int[n]; // Actually, only approx. lgN needed
-
- l = 0; r = n-1; top = stk;
- *top++ = l;
- *top++ = r;
-
- do {
- if (r > l) {
-
- // begin: i = partition(l, r);
- vw = wts[r]; i = l-1; j = r;
- while(1) {
- do { i++; } while(wts[i] > vw);
- do { j--; } while(wts[j] < vw);
- tw = wts[i]; ts = smap[i];
- if (j >= i) {
- wts[i] = wts[j]; wts[j] = tw;
- smap[i] = smap[j]; smap[j] = ts;
- }
- else break;
- }
- wts[i] = wts[r]; wts[r] = tw;
- smap[i] = smap[r]; smap[r] = ts;
- // end: i = partition(l, r)
-
- if ((j)>(r-i)) {
- *top++ = l;
- *top++ = j;
- l = i+1;
- }
- else {
- *top++ = i+1;
- *top++ = r;
- r = j;
- }
- }
- else {
- r = *(--top);
- l = *(--top);
- }
- } while(top != stk);
-
- delete stk;
- }
-
-